% Chakram is a subproject of Hexena - Haskell Executable Analyser
% License:			GPL-3
% Maintainer:		Montez Fitzpatrick
% Stability:		Experimental
% Portability:		GHC
% Module:			Hexena.Codec.Parser.CertificateDirectory

Certificate Directory Format 

All image files that Certificate symbols, including virtually all EXE files 
have an '.idata' section
\begin{code}
module Hexena.Codec.Parser.CertificateDirectory where

import qualified Data.ByteString.Lazy as L
import Data.ByteString (ByteString)
import Data.Binary.Get

import Data.Int (Int64)
import Data.Word

import Control.Monad (replicateM, liftM2)

import Hexena.Data.CertificateDirectory (CertificateDirectory(..), CertificateAttrEntry(..),
										 CertificateDataEntry(..))

-- TODO: Move this to Constants.hs
-- TODO: Create Word128 (octaword size)
oCTAWORD_MAX_BOUNDS :: Integer
oCTAWORD_MAX_BOUNDS = 0xFFFFFFFFFFFFFFFF
oCTAWORD_BYTE_SIZE :: Int
oCTAWORD_BYTE_SIZE = 16

--   params 
--		size: value from the Certificates Table entry in the Optional Header Data Directory
parseCertDirectory :: Word32 -> Get [(CertificateAttrEntry, CertificateDataEntry)]
parseCertDirectory size = do
	cAttr <- parseCertEntries size
	cData <- parseCerts cAttr

	return $ zip cAttr cData

parseCertEntry :: Get CertificateAttrEntry
parseCertEntry = do
	ceLength <- getWord32le
	ceRevision <- getWord16le
	ceType <- getWord16le

	return $ CertificateAttrEntry ceLength ceRevision ceType

-- | Internal Function
--   params 
--		size: value from the Certificates Table entry in the Optional Header Data Directory
parseCertEntries :: Word32 -> Get [CertificateAttrEntry]
parseCertEntries size = replicateM (calcSizeOfCertificateAttr size) parseCertEntry

-- | Internal Function.
--   params 
--		size: value from the Certificates Table entry in the Optional Header Data Directory
calcSizeOfCertificateAttr :: Word32 -> Int
calcSizeOfCertificateAttr size = sizeAsInt `div` byteMultiple
  where
	sizeAsInt = fromIntegral size :: Int
	byteMultiple = 8 :: Int -- sizeOf (certLength + certRevision + certType)

parseCertificate :: Word32 -> Get CertificateDataEntry
parseCertificate unAlignedLength = do
	cert <- getByteString (calcSizeOfCertificateWithAlign unAlignedLength)

	return $ CertificateDataEntry cert

parseCerts :: [CertificateAttrEntry] -> Get [CertificateDataEntry]
parseCerts caAttr = do
	let lengths = fmap (get certLength) caAttr

	mapM parseCertificate lengths

-- | Internal Function
--   *unused*
--   zips two monads together
makeCertBundle :: Get [CertificateAttrEntry] -> Get [CertificateDataEntry] -> Get [(CertificateAttrEntry, CertificateDataEntry)]
makeCertBundle aEntries dEntries = liftM2 zip aEntries dEntries

-- | Internal Function.  Uses what was gathered with the attribute table to
--   calculate the size of each subsequent certificate.  The certificates will be
--   aligned with an OctaWord (128-bit) offset.
--
--   params
--		length: ceLength from Attribute Table
--
--	 returns the number of bytes as an Int to be read
--	 TODO: Check source of ceLength for possible bug
calcSizeOfCertificateWithAlign :: Word32 -> Int
calcSizeOfCertificateWithAlign length = fromIntegral $ case (lenAsInt `compare` oCTAWORD_BYTE_SIZE) of
	GT -> (base + remains) * oCTAWORD_BYTE_SIZE
	otherwise -> 1 * oCTAWORD_BYTE_SIZE
  where
	lenAsInt = fromIntegral length :: Int
	base = lenAsInt `div` oCTAWORD_BYTE_SIZE
	remains -- needing to account for alignment
		| lenAsInt `rem` oCTAWORD_BYTE_SIZE > 0	= 1
		| otherwise								= 0
\end{code}
